#!/usr/bin/env perl
use strict;
use FindBin;

use File::Basename;
use Cwd 'abs_path';
use Getopt::Long;
use File::Temp;
use Digest::MD5 qw(md5 md5_hex);
use JSON qw(to_json from_json);

use SSHRemoteExec;
use SCPExec;
use DeployUtils;

Getopt::Long::Configure("pass_through");

sub usage {
    my $pname = $FindBin::Script;

    print("Usage: $pname [--verbose 0|1]\n");
    print("              [--supass SuPassword] [--eofstr EOFString] [--failstr FailString]\n");
    print("              [--timeout TimeoutSecs] [-o SSHOptions]\n");
    print("              [--destencoding TargetOsEncoding]\n");
    print("              --interpreter sh|perl|... --script ScriptJson\n");
    print("\n");
    print("       --node:         Host node json\n");
    print("       --supass:       su execute user password\n");
    print("       --eofstr:       when command outpute match eofstr, the execute will be terminated\n");
    print("       --failstr:      when command outpute match failstr, the execute will be terminated and return faled\n");
    print("       --timeout:      tiemout for ssh execute\n");
    print("       --o:            ssh options, example: \"SendEnv EXEC_PASSWORD;ForwardX11 yes\"\n");
    print("       --destencoding: Target OS encoding\n");
    print("       --interpreter:  Script interperter, such as sh|perl|vbs|powershell\n");
    print("       --script:       Script content, example: '#!/bin/bash\\necho helloworld!\\n\"'\n");

    exit(1);
}

sub main {
    my ( $isHelp, $isVerbose, $envPath, $version );
    my ( $node, $destCharset, $supass, $sshOpts, $scriptContent, $eofStr, $failStr, $timeOut );
    my $interpreter = 'sh';
    $destCharset = 'utf-8';
    $isVerbose   = 0;

    GetOptions(
        'h|help'         => \$isHelp,
        'v|verbose=i'    => \$isVerbose,
        'destencoding=s' => \$destCharset,
        'node=s'         => \$node,
        'supass=s'       => \$supass,
        'eofstr=s'       => \$eofStr,
        'failstr=s'      => \$failStr,
        'timeout=i'      => \$timeOut,
        'o=s'            => \$sshOpts,
        'interpreter=s'  => \$interpreter,
        'script=s'       => \$scriptContent
    );

    usage() if ( defined($isHelp) );
    my $deployUtils = DeployUtils->new();

    my $optionError = 0;

    my $nodeInfo = $deployUtils->getNodeInfo($node);
    if ( not $nodeInfo ) {
        $optionError = 1;
        print("ERROR: Execute node json not defined by environment AUTOEXEC_NODE or option --node\n");
    }

    if ( $destCharset eq '' ) {
        $destCharset = 'utf-8';
    }
    else {
        $destCharset = lc($destCharset);
    }

    if ( defined($eofStr) ) {
        if ( $eofStr =~ /^\{([\w-\d]+)\}/ ) {
            $eofStr =~ s/^\{[\w-\d]+\}//;
            $eofStr = Encode::encode( lc($1), Encode::decode( 'utf-8', $eofStr ) );
        }
        elsif ( $destCharset ne 'utf-8' ) {
            $eofStr = Encode::encode( lc($destCharset), Encode::decode( 'utf-8', $eofStr ) );
        }
    }

    if ( defined($failStr) ) {
        if ( $failStr =~ /^\{([\w-\d]+)\}/ ) {
            $failStr =~ s/^\{[\w-\d]+\}//;
            $failStr = Encode::encode( lc($1), Encode::decode( 'utf-8', $failStr ) );
        }
        elsif ( $destCharset ne 'utf-8' ) {
            $failStr = Encode::encode( lc($destCharset), Encode::decode( 'utf-8', $failStr ) );
        }
    }

    if ( not defined($scriptContent) ) {
        $optionError = 1;
        print("ERROR: Must defined script content with --script option.\n");
    }

    if ( $optionError == 1 ) {
        usage();
    }

    if ( $destCharset ne 'utf-8' ) {
        $scriptContent = Encode::encode( lc($destCharset), Encode::decode( 'utf-8', $scriptContent ) );
    }
    $scriptContent =~ s/\\n/\n/sg;

    my $scriptInterpreter = $interpreter;
    my $arguments         = '';
    foreach my $arg (@ARGV) {
        $arguments = qq{$arguments "$arg"};
    }

    while ( $arguments =~ /(?<!\\)\$(\w+)/g ) {
        my $varName = $1;
        if ( exists( $ENV{$varName} ) ) {
            $arguments =~ s/(?<!\\)\$$varName/$ENV{$varName}/g;
        }
    }

    while ( $arguments =~ /(?<!\\)\$\{(\w+)\}/g ) {
        my $varName = $1;
        if ( exists( $ENV{$varName} ) ) {
            $arguments =~ s/(?<!\\)\$\{$varName\}/$ENV{$varName}/g;
        }
    }

    my $TMPDIR  = $ENV{AUTOEXEC_HOME} . '/tmp';
    my $tmp     = File::Temp->new( DIR => $TMPDIR, SUFFIX => '' );
    my $tmpPath = $tmp->filename;
    print $tmp ($scriptContent);
    $tmp->flush();

    my $scriptName = basename($tmpPath) . $deployUtils->getScriptExtName($scriptInterpreter);

    my $hasError = 0;

    my $host    = $nodeInfo->{host};
    my $port    = $nodeInfo->{protocolPort};
    my $user    = $nodeInfo->{username};
    my $pass    = $nodeInfo->{password};
    my $insId   = $nodeInfo->{resourceId};
    my $insName = $nodeInfo->{nodeName};

    if ( defined($pass) and $pass ne '' ) {
        $pass = $deployUtils->decryptPwd($pass);
    }

    my $scpExec = new SCPExec(
        host        => $host,
        port        => $port,
        username    => $user,
        password    => $pass,
        source      => $tmpPath,
        destination => "$user\@$host:/tmp/$scriptName",
        verbose     => $isVerbose
    );
    my $rc = $scpExec->exec();

    if ( $rc != 0 ) {
        print("ERROR: Copy scirpt to /tmp/$scriptName failed.\n");
        exit(1);
    }

    my $cmd     = "$scriptInterpreter /tmp/$scriptName $arguments; rc=\$?; rm -f /tmp/$scriptName; exit \$rc";
    my $sshExec = new SSHRemoteExec(
        host       => $host,
        port       => $port,
        username   => $user,
        password   => $pass,
        verbose    => $isVerbose,
        cmd        => $cmd,
        scriptFile => undef,
        eofStr     => $eofStr,
        failStr    => $failStr,
        supassword => $supass,
        timeout    => $timeOut,
        sshOpts    => $sshOpts
    );

    my $ret = $sshExec->exec();
    $hasError = $hasError + $ret;
    if ( $sshExec->{status} eq 'SUCCESS' ) {
        print("FINE: SSH remote exec success.\n");
    }
    elsif ( $sshExec->{status} eq 'WARN' ) {
        print("WARN: SSH remote exec has warning.\n");
    }
    elsif ( $sshExec->{status} eq 'ERROR' ) {
        $hasError = $hasError + 1;
        print("ERROR: SSH remote exec has error.\n");
    }

    return $hasError == 0 ? 0 : 1;
}

exit main();

